package com.content.scaffold.common.interceptor;

import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.content.scaffold.common.annotation.SensitiveField;
import com.content.scaffold.common.utils.AesEncryptUtil;
import com.content.scaffold.common.utils.TestUtil;
import com.content.scaffold.model.enums.SensitiveType;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.Statement;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author xiaojl
 */
@Intercepts({
        @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class}),
        @Signature(type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class})
})
@Slf4j
public class SensitiveInterceptor implements Interceptor {

    /**
     * 缓存类对应的敏感字段
     */

    private static final Map<Class<?>, Set<Field>> FIELD_CACHE = new ConcurrentHashMap<>();

    /**
     * 敏感字段关键词集合
     */

    private static final Set<String> SENSITIVE_KEYWORDS = Set.of("phone", "cardNo", "bank","mobile");

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 1. 处理查询结果脱敏
        if (invocation.getTarget() instanceof ResultSetHandler) {
            List<?> results = (List<?>) invocation.proceed();
            return results.stream().map(this::processResult).collect(Collectors.toList());
        }
        // 2. 处理参数加密
        else if (invocation.getTarget() instanceof ParameterHandler) {
            MetaObject metaObject = SystemMetaObject.forObject(invocation.getArgs()[0]);
            encryptParameters(metaObject);
        }
        return invocation.proceed();
    }

    private Object processResult(Object result) {
        if (result == null) {
            return null;
        }
        try {
            Set<Field> sensitiveFields = getFieldCache(result.getClass());
            for (Field field : sensitiveFields) {
                field.setAccessible(true);
                Object value = field.get(result);
                if (value instanceof String) {
                    SensitiveType type = field.getAnnotation(SensitiveField.class).type();
                    boolean encryption = field.getAnnotation(SensitiveField.class).encryption();
                    if (BooleanUtil.isTrue(encryption)){
                        log.info("加密后数据：{}",value);
                        String decrypt = AesEncryptUtil.decrypt((String) value);
                        log.info("解密后数据：{}",decrypt);
                        field.set(result, type.desensitize(decrypt));
                    }else {
                        field.set(result, type.desensitize((String) value));
                    }

                }
            }
        } catch (Exception e) {
            throw new RuntimeException("脱敏处理失败", e);
        }
        return result;
    }

    private void encryptParameters(MetaObject metaObject) {
        if (metaObject == null) {
            return;
        }

        Object originalObject = metaObject.getOriginalObject();
        if (originalObject == null) {
            return;
        }

        if (originalObject instanceof Map<?, ?> paramMap) {
            paramMap.forEach((key, value) -> {
                if (value instanceof String) {
                    String paramName = key.toString();
                    if (isSensitiveParam(paramName)) {
                        metaObject.setValue(paramName, encrypt((String) value));
                    }
                }
            });
        } else {
            //AopProxyUtils.ultimateTargetClass(metaObject.getOriginalObject());
            Class<?> clazz =
            TestUtil.resolveRealClass(metaObject.getOriginalObject());
            Set<Field> sensitiveFields = getFieldCache(clazz);
            for (Field field : sensitiveFields) {
                try {
                    field.setAccessible(true);
                    Object value = field.get(originalObject);
                    if (value instanceof String) {
                        SensitiveType type = field.getAnnotation(SensitiveField.class).type();
                        boolean encryption = field.getAnnotation(SensitiveField.class).encryption();
                        if (BooleanUtil.isTrue(encryption)){
                            field.set(originalObject, AesEncryptUtil.encrypt((String) value));
                        }else {
                            field.set(originalObject, type.desensitize((String) value));
                        }

                    }
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("参数加密失败", e);
                }
            }
        }
    }

    /**
     * 获取类中标记为敏感字段的字段列表（带缓存）
     */
    private Set<Field> getFieldCache(Class<?> clazz) {
        return FIELD_CACHE.computeIfAbsent(clazz, cls -> {
            Set<Field> fieldList = new HashSet<>();
            while (cls != null && cls != Object.class) {
                Field[] declaredFields = clazz.getDeclaredFields();
                for (Field field : declaredFields) {
                    SensitiveField annotation = field.getAnnotation(SensitiveField.class);
                    if (ObjectUtil.isNotEmpty(annotation)){
                        fieldList.add(field);
                    }

                }
                cls = cls.getSuperclass();
            }
            return fieldList;
        });
    }

    /**
     * 判断参数名是否为敏感字段
     */
    private boolean isSensitiveParam(String paramName) {
        String lowerCaseName = paramName.toLowerCase();
        return SENSITIVE_KEYWORDS.stream().anyMatch(lowerCaseName::contains);
    }

    /**
     * 模拟加密逻辑（应替换为实际加密算法）
     */
    private String encrypt(String value) {
        return value.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")
                .replaceAll("(\\d{4})\\d{10}(\\w{4})", "$1**********$2");
    }
}
